Web Animations APIことはじめ
そもそもWeb Animations API(WAAPI)とは?
乱立するアニメーションの表現方法(Animation Libraries, CSS Animations)を一つにまとめ、ブラウザネイティブレベルで実装することにより、パフォーマンスの向上も狙ったもと登場したAPI
従来のアニメーション表現方法との比較
CSS Animation
ブラウザに組み込まれた機能の一部で、スムーズなトランジション表現のためGPUでの処理を可能としているが、CSSに記述するため、アニメーションのタイミングや動的な調整など、細かい表現が難しい
requestAnimationFrame
ブラウザでサポートされており、状況に応じた最適なアニメーションを提供できる一方で、他のJSで処理がもたついた場合、fpsが安定しないことがある
setTimeout / setInterval
requestAnimationFrameに対して、一定のfpsでアニメーションを描画できるが、タブの非活性時など常に一定のタイミングで走り続けるのでパフォーマンスに影響しがち
jQuery.animate()
パフォーマンス悪め
外部ライブラリに依存するため保守大変
どうやって使うの
ブラウザ標準のAPIなので、基本的には特別な準備は不要
ただし、Firefox 48+, Chrome 36+, Safari 13.1+以外のブラウザでは対応していないので、polyfillを導入する必要がある polyfill導入すればほぼモダンブラウザで動く
- Chrome 56+
- Firefox 27+
- IE10+ (including Edge)
- Safari (iOS) 7.1+
- Safari (Mac) 9+
https://gyazo.com/a3a120164208a58230e80b81e36dbe0b
やってみよう
まずはお馴染みのCSS Animationと比較
code: css
animation: ballSliding 2000ms ease-in-out alternate infinite;
@keyframes ballSliding {
0% {
transform: translateX(0);
border-radius: 100%;
}
50% {
transform: translateX(calc(50vw - 50%));
border-radius: 30%;
}
100% {
transform: translateX(calc(100vw - 100%));
border-radius: 0;
background-color: orange;
}
}
code: js
// アニメーションを定義(CSSの@keyframesのようなもの)
const ballSliding = [
{ transform: "translateX(0)", borderRadius: "100%", easing: "ease-in-out" },
{
transform: "translateX(calc(50vw - 50%))",
borderRadius: "30%",
easing: "ease-in-out"
},
{
transform: "translateX(calc(100vw - 100%))",
borderRadius: "0",
backgroundColor: "orange",
easing: "ease-in-out"
}
];
// アニメーションの設定
const ballTiming = {
duration: 2000,
iterations: Infinity,
direction: "alternate",
easing: "ease-in-out"
};
document.getElementById("ball").animate(ballSliding, ballTiming);
違い
- WAAPIではミリ秒の指定のみ可能
- Animationの実行が不可能(キーが一個しかない等)の場合、NotSupportedErrorをスローしてくれる
- WAAPIではCSSで必要なキーの打ち込みが必須ではない(均等に配置される)
キーを指定したい場合、CSSプロパティにoffsetを追加する
code: js
{
transform: "translateX(calc(50vw - 50%))",
borderRadius: "30%",
easing: "ease-in-out",
offset: 0.3
},
アニメーションのコントロール
animate関数を定義することで、Animaitonオブジェクトのインスタンスが返ってくるのでそれを用いてタイムラインのコントロールが可能 code: js
const ballAnimation = document
.getElementById("ball")
.animate(foo, bar);
// イベントに紐づけて呼び出し
ballAnimation.play();
アニメーションの重ね掛け
アニメーションをプロパティ毎に分割することが可能、それぞれに対してAnimationオブジェクトが返ってくるので、それぞれに対して、制御が可能
code: js
const slideZ = [
{ transform: "translateZ(80px) scale(1)" },
{ transform: "translateZ(0) scale(1.3)" }
];
const fadeInOut = opacity: 0.3 }, { opacity: 1 };
el.animate(fadeInOut, timings);
el.animate(slideZ, timings);
CSSアニメーションとの連携
element.getAnimations()を使えばCSSアニメーションのコントロールも可能に
code: js
// CSSアニメーション含めた全てのアニメーションが配列として返ってくる
const ballAnimations = document.getElementById("ball").getAnimations();
ballAnimations.forEach((animation) => {
window.addEventListener("mousemove", (e) => {
animation.playbackRate = e.clientX / window.innerWidth;
});
});
連続発火するイベントに紐づいたアニメーションの生成(Chromium83)
要素に対して連続的にプロパティを変化させた場合、前回のアニメーションとの差分を算出して置き換え、更新前のアニメーションは削除される仕組み
かなりヌルヌル動く
code: js
document.addEventListener("mousemove", (e) => {
elms.forEach(elm => {
elm.animate(
{
transform: translation
},
...
);
});
});
まとめ
シンプルな記述スピードコントロールなど、今までになかった表現もハイパフォーマンスに動かすことができる一方で、複数アニメーションを用いたタイムラインコントロールなど、まだまだ草案段階の仕様も多いため、実用性には少し欠ける。
ただし、Polyfillを導入することで幅広いブラウザにも対応することができるため、シンプルなアニメーションを記述する際には選択肢の一つとして考えられそう。
作品例